import { ElMessage } from "element-plus";
import type { App } from "vue";

// WebSocket状态枚举
export enum WsStatus {
  CONNECTING = 0,
  OPEN = 1,
  CLOSING = 2,
  CLOSED = 3
}

// WebSocket配置选项
export interface WsOptions {
  url: string;
  reconnect?: boolean;
  reconnectInterval?: number;
  maxReconnectAttempts?: number;
  autoConnect?: boolean;
  protocols?: string | string[];
}

// WebSocket事件类型
export type WsEvent = "open" | "message" | "error" | "close";

// 默认配置
const DEFAULT_OPTIONS: WsOptions = {
  url: "",
  reconnect: true,
  reconnectInterval: 3000,
  maxReconnectAttempts: 10,
  autoConnect: true
};

/**
 * WebSocket工具类
 * 提供连接管理、自动重连、消息发送/接收等功能
 */
export class WebSocketService {
  private ws: WebSocket | null = null;
  private options: WsOptions;
  private status: WsStatus = WsStatus.CLOSED;
  private reconnectAttempts = 0;
  private eventListeners: Map<WsEvent, Function[]> = new Map();
  private messageQueue: any[] = [];
  private reconnectTimer: NodeJS.Timeout | null = null;

  constructor(options: Partial<WsOptions> = {}) {
    this.options = { ...DEFAULT_OPTIONS, ...options };

    if (!this.options.url) {
      throw new Error("WebSocket URL is required");
    }

    if (this.options.autoConnect) {
      this.connect();
    }
  }

  /**
   * 建立WebSocket连接
   */
  connect(): void {
    if (this.status !== WsStatus.CLOSED) {
      return;
    }

    try {
      this.ws = new WebSocket(this.options.url, this.options.protocols);
      this.status = WsStatus.CONNECTING;

      this.ws.onopen = event => this.handleOpen(event);
      this.ws.onmessage = event => this.handleMessage(event);
      this.ws.onerror = event => this.handleError(event);
      this.ws.onclose = event => this.handleClose(event);
    } catch (error) {
      ElMessage.error(
        `WebSocket连接失败: ${error instanceof Error ? error.message : String(error)}`
      );
      this.status = WsStatus.CLOSED;
      this.attemptReconnect();
    }
  }

  /**
   * 关闭WebSocket连接
   * @param code 状态码
   * @param reason 原因
   */
  close(code?: number, reason?: string): void {
    if (this.ws && this.status !== WsStatus.CLOSED) {
      this.ws.close(code, reason);
      this.status = WsStatus.CLOSING;
    }

    // 清除重连定时器
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
  }

  /**
   * 发送消息
   * @param data 要发送的数据
   * @param binary 是否发送二进制数据
   */
  send(data: any, binary = false): void {
    if (this.status !== WsStatus.OPEN || !this.ws) {
      // 如果连接未打开，将消息加入队列
      this.messageQueue.push({ data, binary });
      ElMessage.warning("WebSocket连接未建立，消息已加入队列");
      return;
    }

    try {
      if (binary) {
        this.ws.send(data);
      } else {
        this.ws.send(typeof data === "string" ? data : JSON.stringify(data));
      }
    } catch (error) {
      ElMessage.error(
        `消息发送失败: ${error instanceof Error ? error.message : String(error)}`
      );
    }
  }

  /**
   * 添加事件监听器
   * @param event 事件类型
   * @param listener 监听器函数
   */
  on(event: WsEvent, listener: Function): void {
    if (!this.eventListeners.has(event)) {
      this.eventListeners.set(event, []);
    }
    this.eventListeners.get(event)!.push(listener);
  }

  /**
   * 移除事件监听器
   * @param event 事件类型
   * @param listener 监听器函数，如果不提供则移除所有
   */
  off(event: WsEvent, listener?: Function): void {
    if (!this.eventListeners.has(event)) return;

    if (listener) {
      this.eventListeners.set(
        event,
        this.eventListeners.get(event)!.filter(l => l !== listener)
      );
    } else {
      this.eventListeners.delete(event);
    }
  }

  /**
   * 获取当前连接状态
   */
  getStatus(): WsStatus {
    return this.status;
  }

  /**
   * 检查是否已连接
   */
  isConnected(): boolean {
    return this.status === WsStatus.OPEN;
  }

  /**
   * 处理连接打开事件
   */
  private handleOpen(event: Event): void {
    this.status = WsStatus.OPEN;
    this.reconnectAttempts = 0;
    ElMessage.success("WebSocket连接成功");

    // 发送队列中的消息
    this.flushMessageQueue();

    // 触发open事件
    this.triggerEvent("open", event);
  }

  /**
   * 处理消息接收事件
   */
  private handleMessage(event: MessageEvent): void {
    try {
      // 尝试解析JSON消息
      const data =
        event.data instanceof Blob ? event.data : JSON.parse(event.data);
      this.triggerEvent("message", data, event);
    } catch (error) {
      // 如果不是JSON格式，直接传递原始数据
      this.triggerEvent("message", event.data, event);
    }
  }

  /**
   * 处理错误事件
   */
  private handleError(event: Event | ErrorEvent): void {
    ElMessage.error(
      `WebSocket错误: ${event instanceof ErrorEvent ? event.message : "未知错误"}`
    );
    this.triggerEvent("error", event);
  }

  /**
   * 处理连接关闭事件
   */
  private handleClose(event: CloseEvent): void {
    this.status = WsStatus.CLOSED;
    this.ws = null;

    // 触发close事件
    this.triggerEvent("close", event);

    // 如果不是正常关闭(1000)且启用了重连，则尝试重连
    if (event.code !== 1000 && this.options.reconnect) {
      this.attemptReconnect();
    } else if (event.code === 1000) {
      ElMessage.info("WebSocket连接已正常关闭");
    } else {
      ElMessage.error(`WebSocket连接已关闭: ${event.reason || "未知原因"}`);
    }
  }

  /**
   * 尝试重连
   */
  private attemptReconnect(): void {
    if (
      !this.options.reconnect ||
      this.reconnectAttempts >= this.options.maxReconnectAttempts!
    ) {
      ElMessage.error("WebSocket重连失败次数过多，已停止尝试");
      return;
    }

    this.reconnectAttempts++;
    const delay = this.options.reconnectInterval! * this.reconnectAttempts;
    ElMessage.warning(
      `WebSocket尝试重连(${this.reconnectAttempts}/${this.options.maxReconnectAttempts!})...`
    );

    this.reconnectTimer = setTimeout(() => {
      this.connect();
    }, delay);
  }

  /**
   * 发送队列中的消息
   */
  private flushMessageQueue(): void {
    if (
      this.messageQueue.length === 0 ||
      this.status !== WsStatus.OPEN ||
      !this.ws
    ) {
      return;
    }

    ElMessage.info(`正在发送队列中的${this.messageQueue.length}条消息`);
    while (this.messageQueue.length > 0) {
      const { data, binary } = this.messageQueue.shift()!;
      this.send(data, binary);
    }
  }

  /**
   * 触发事件
   */
  private triggerEvent(event: WsEvent, ...args: any[]): void {
    if (this.eventListeners.has(event)) {
      this.eventListeners.get(event)!.forEach(listener => {
        try {
          listener(...args);
        } catch (error) {
          console.error(
            `WebSocket事件监听器错误: ${error instanceof Error ? error.message : String(error)}`
          );
        }
      });
    }
  }
}

// 导出插件形式，方便在Vue中全局使用
export const WebSocketPlugin = {
  install: (app: App, options: Partial<WsOptions> = {}) => {
    const wsService = new WebSocketService(options);
    app.config.globalProperties.$ws = wsService;
    app.provide("ws", wsService);
  }
};

export default WebSocketService;
